﻿using Furion;
using Furion.DatabaseAccessor;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.UnifyResult;
using JoyAdmin.Core;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using TulingMember.Application.Dto;
using TulingMember.Application.Util;
using TulingMember.Core;
using Yitter.IdGenerator;

namespace TulingMember.Application
{
    public class ProductService : IDynamicApiController
    {
        private readonly ILogger _logger;
        private readonly IRepository<cts_Product> _productRepository;
        private readonly IRepository<cts_ProductType> _producttypeRepository;
        private readonly IRepository<cts_Unit> _unitRepository;
        private readonly IRepository<cts_ProductLog> _productlogRepository;
        public ProductService(ILogger logger
            , IRepository<cts_Product> productRepository
            , IRepository<cts_ProductType> producttypeRepository
            , IRepository<cts_Unit> unitRepository
            , IRepository<cts_ProductLog> productlogRepository)
        {
            _logger = logger;
            _productRepository = productRepository;
            _producttypeRepository = producttypeRepository;
            _unitRepository = unitRepository;
            _productlogRepository = productlogRepository;
        }

        #region 商品分类
        /// <summary>
        /// 列表
        /// </summary> 
        public List<cts_ProductType> SearchProductType(BaseInput input)
        {
            var search = _producttypeRepository.AsQueryable();
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword));
            }
            return search.OrderBy(m => m.Sort).ToList();
        }
        /// <summary>
        /// 获取树形商品分类
        /// </summary>
        /// <returns></returns>
        public List<TreeProductTypeDto> GetTreeProductType()
        {
            var list = _producttypeRepository.AsQueryable().Select(u => u.Adapt<TreeProductTypeDto>()).ToList();
            return new TreeBuildUtil<TreeProductTypeDto>().Build(list);
        }




        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [SecurityDefine("product")]
        [UnitOfWork]
        public void SaveProductType(cts_ProductType input)
        {
            var oldCount = _producttypeRepository.Where(m => m.Name.Equals(input.Name) && m.Id != input.Id).Count();
            if (oldCount > 0)
            {
                throw Oops.Bah("药品分类已存在，请检查").StatusCode(ErrorStatus.ValidationFaild);
            }
            input.ParentIds = CreateNewPids(input.ParentId);
            if (input.Id > 0)
            {
                _producttypeRepository.Update(input);
            }
            else
            {
                _producttypeRepository.Insert(input);
            }

        }
        /// <summary>
        /// 删除
        /// </summary>  
        [SecurityDefine("product")]
        public void DeleteProductType(long id)
        {
            var productCount = _productRepository.Where(m => m.TypeId == id).Count();
            if (productCount > 0)
            {
                throw Oops.Bah("该分类下存在药品，请检查").StatusCode(ErrorStatus.ValidationFaild);
            }
            _producttypeRepository.FakeDelete(id);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pid"></param>
        /// <returns></returns>
        [NonAction]
        private string CreateNewPids(long pid)
        {
            if (pid == 0L)
            {
                return "[0],";
            }
            else
            {
                var pmenu = _producttypeRepository.DetachedEntities.FirstOrDefault(u => u.Id == pid);
                return pmenu.ParentIds + "[" + pid + "],";
            }
        }
        #endregion
        #region 商品管理
        /// <summary>
        /// 产品出入库统计
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public PagedList<ProductStatisticsDto> SearchStatisticsProduct(BaseInput input)
        {
            var search = _productRepository.Include(s => s.PurchaseOrderDetails)
                .Include(s => s.SaleOrderDetails)
                .AsQueryable();
            search = search.Where(x => x.ExpirationDate >= DateTime.Now);
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword)
                || m.Code.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }
            if (input.sdate != null)
            {
                search = search.Where(m => m.CreatedTime >= input.sdate);
            }
            if (input.edate != null)
            {
                var edate = input.edate?.AddDays(1).Date;
                search = search.Where(m => m.CreatedTime < edate);
            }
            var data = search.OrderByDescending(x => x.CreatedTime).ToPagedList(input.page, input.size);

            var result = new PagedList<ProductStatisticsDto>
            {
                PageIndex = data.PageIndex,
                PageSize = data.PageSize,
                TotalCount = data.TotalCount,
                TotalPages = data.TotalPages,
                HasNextPages = data.HasNextPages,
                HasPrevPages = data.HasPrevPages
            };

            result.Items = data.Items.Select(s => new ProductStatisticsDto
            {
                Id = s.Id,
                Name = s.Name,
                Num = s.Num,
                Specification = s.Specification,
                Description = s.Description,
                ExpirationDate = s.ExpirationDate,
                PurchaseVisits = s.PurchaseOrderDetails.GroupBy(s => s.PurchaseOrderId).Count(),
                PurchaseNum = s.PurchaseOrderDetails.Sum(m => m.Num),
                PurchaseAmount = s.PurchaseOrderDetails.Sum(m => m.Price * m.Num),
                SaleVisits = s.SaleOrderDetails.GroupBy(s => s.SaleOrderId).Count(),
                SaleNum = s.SaleOrderDetails.Sum(m => m.Num),
                SaleAmount = s.SaleOrderDetails.Sum(m => m.Price * m.Num),
            });
            return result;
        }

        /// <summary>
        /// 查询即将过期的药品
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public PagedList<ProductDto> SearchExpiredProduct(BaseInput input)
        {
            var expriedTime = DateTime.Now.AddDays(180);
            var search = _productRepository.AsQueryable();
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword)
                || m.Code.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }
            if (input.sdate != null)
            {
                search = search.Where(m => m.ExpirationDate >= input.sdate);
            }
            if (input.edate != null)
            {
                var edate = input.edate?.AddDays(1).Date;
                search = search.Where(m => m.ExpirationDate < edate);
            }
            search = search.Where(s => expriedTime > s.ExpirationDate);

            var search2 = from a in search
                          join b in _producttypeRepository.AsQueryable() on a.TypeId equals b.Id
                          orderby a.ExpirationDate, a.Name
                          select DtoHelper.TransProductDto(a, b);
            //search2.OrderBy(x => x.ExpirationDate);
            return search2.ToPagedList(input.page, input.size);
        }

        /// <summary>
        /// 商品列表
        /// </summary> 
        public PagedList<ProductDto> SearchProduct(BaseInput input)
        {
            var search = _productRepository.AsQueryable();

            search = search.Where(x => x.ExpirationDate >= DateTime.Now);
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Code.Contains(input.keyword)
                || m.Name.Contains(input.keyword) || m.Specification.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }

            var search2 = from a in search
                          join b in _producttypeRepository.AsQueryable() on a.TypeId equals b.Id
                          orderby a.ExpirationDate, a.Name
                          select DtoHelper.TransProductDto(a, b);
            //search2.OrderBy(x => x.ExpirationDate).ThenBy(x => x.Name);
            return search2.ToPagedList(input.page, input.size);
        }

        /// <summary>
        /// 保存商品
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [SecurityDefine("product")]
        [UnitOfWork]
        public void SaveProduct(cts_Product input)
        {
            var oldCount = _productRepository.Where(m => m.Name.Equals(input.Name) && m.Id != input.Id)
                .Where(m => m.Specification.Equals(input.Specification) && m.Formulations.Equals(input.Formulations) && m.Price.Equals(input.Price) && m.ExpirationDate.Equals(input.ExpirationDate) && m.Id != input.Id).Count();
            if (oldCount > 0)
            {
                throw Oops.Bah("该药品已存在，请检查").StatusCode(ErrorStatus.ValidationFaild);
            }
            var ProductType = _producttypeRepository.AsQueryable(x => x.ParentId == 0 && !x.IsDeleted).FirstOrDefault();
            input.TypeId = ProductType.Id;
            if (input.Id > 0)
            {
                _productRepository.UpdateExclude(input, new string[] { nameof(input.Num) });
            }
            else
            {
                input.Id = YitIdHelper.NextId();
                _productRepository.Insert(input);
                if (input.Num > 0)
                {
                    _productlogRepository.Insert(new cts_ProductLog
                    {
                        ProductId = input.Id,
                        ProductName = input.Name,
                        ChangeNum = input.Num,
                        NewNum = input.Num,
                        TagName = "期初库存",
                        TagType = ProductChangeTag.None
                    });
                }
            }
            if (!string.IsNullOrEmpty(input.Unit))
            {
                var unitCount = _unitRepository.Where(m => m.Name.Equals(input.Unit)).Count();
                if (unitCount == 0)
                {
                    _unitRepository.Insert(new cts_Unit { Name = input.Unit });
                }
            }

        }
        /// <summary>
        /// 删除商品
        /// </summary>  
        [SecurityDefine("product")]
        public void DeleteProduct(long id)
        {
            _productRepository.FakeDelete(id);
        }

        /// <summary>
        /// 产品出入库明细
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public PagedList<cts_ProductLog> SearchProductLog(BaseInput input)
        {

            var search = _productlogRepository.Where(m => m.ProductId == input.productid);
            if (input.sdate != null)
            {
                search = search.Where(m => m.CreatedTime >= input.sdate);
            }
            if (input.edate != null)
            {
                var edate = input.edate?.AddDays(1).Date;
                search = search.Where(m => m.CreatedTime < edate);
            }
            search = search.OrderByDescending(m => m.CreatedTime);
            var data = search.ToPagedList(input.page, input.size);
            return data;
        }
        /// <summary>
        /// 盘点
        /// </summary> 
        [SecurityDefine("productcheck")]
        [UnitOfWork]
        public Task SaveProductCheck(ProductCheckDto dto)
        {
            var oldentity = _productRepository.FindOrDefault(dto.Id);
            if (oldentity.Num != dto.CheckNum)
            {
                //变更记录
                cts_ProductLog log = new cts_ProductLog
                {
                    Id = YitIdHelper.NextId(),
                    ProductId = oldentity.Id,
                    ProductName = oldentity.Name,
                    OldNum = oldentity.Num,
                    ChangeNum = dto.CheckNum - oldentity.Num,
                    NewNum = dto.CheckNum,
                    TagType = ProductChangeTag.Check,
                    TagName = "盘点",
                };
                _productlogRepository.Insert(log);
                oldentity.Num = dto.CheckNum;
            }
            return Task.CompletedTask;
        }
        #endregion
        #region 计量单位

        /// <summary>
        /// 列表
        /// </summary> 
        public List<cts_Unit> SearchUnit(BaseInput input)
        {
            var search = _unitRepository.AsQueryable();
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword));
            }
            return search.OrderBy(m => m.Sort).ToList();
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [SecurityDefine("product")]
        [UnitOfWork]
        public void SaveUnit(cts_Unit input)
        {
            var oldCount = _unitRepository.Where(m => m.Name.Equals(input.Name) && m.Id != input.Id).Count();
            if (oldCount > 0)
            {
                throw Oops.Bah("单位已存在，请检查").StatusCode(ErrorStatus.ValidationFaild);
            }

            if (input.Id > 0)
            {
                _unitRepository.Update(input);
            }
            else
            {
                _unitRepository.Insert(input);
            }

        }
        /// <summary>
        /// 删除
        /// </summary>  
        [SecurityDefine("product")]
        public void DeleteUnit(long id)
        {
            _unitRepository.Delete(id);
        }
        #endregion
        #region 导出
        /// <summary>
        /// 导出过期、临期药品
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> ExportExpiredProduct([FromQuery] BaseInput input)
        {
            List<ProductExport> data = this.GetExportExpiredProduct(input);
            var fileStream = ExcelHelper.ParseListToExcel(data);
            var fileName = HttpUtility.UrlEncode("临期药品_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx", Encoding.GetEncoding("UTF-8"));
            return await Task.FromResult(
             new FileContentResult(fileStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
             {
                 FileDownloadName = fileName
             });
        }

        /// <summary>
        /// 查询即将过期的药品-导出
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private List<ProductExport> GetExportExpiredProduct(BaseInput input)
        {
            List<ProductExport> productExportData = new List<ProductExport>();
            var expriedTime = DateTime.Now.AddDays(180);
            var search = _productRepository.AsQueryable();
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword)
                || m.Code.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }
            if (input.sdate != null)
            {
                search = search.Where(m => m.ExpirationDate >= input.sdate);
            }
            if (input.edate != null)
            {
                var edate = input.edate?.AddDays(1).Date;
                search = search.Where(m => m.ExpirationDate < edate);
            }
            search = search.Where(s => expriedTime > s.ExpirationDate);

            var search2 = from a in search
                          join b in _producttypeRepository.AsQueryable() on a.TypeId equals b.Id
                          orderby a.ExpirationDate
                          select DtoHelper.TransProductDto(a, b);

            productExportData = search2.Select(s => new ProductExport
            {
                Name = s.Name,
                Specification = s.Specification,
                ExpirationDate = s.ExpirationDate,
                Num = s.Num,
                Formulations = s.Formulations,
                Manufacturer = s.Manufacturer,
                Price = s.Price,
                Unit = s.Unit
            }).ToList();
            return productExportData;
        }
        /// <summary>
        /// 导出药品
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> ExportProduct([FromQuery] BaseInput input)
        {
            List<ProductExport> data = this.GetExportProduct(input);
            var fileStream = ExcelHelper.ParseListToExcel(data);
            var fileName = HttpUtility.UrlEncode("药品_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx", Encoding.GetEncoding("UTF-8"));
            return await Task.FromResult(
             new FileContentResult(fileStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
             {
                 FileDownloadName = fileName
             });
        }

        /// <summary>
        /// 查询药品-导出
        /// </summary> 
        private List<ProductExport> GetExportProduct(BaseInput input)
        {
            List<ProductExport> productExportData = new List<ProductExport>();
            var search = _productRepository.AsQueryable();
            search = search.Where(x => x.ExpirationDate >= DateTime.Now);
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Code.Contains(input.keyword)
                || m.Name.Contains(input.keyword) || m.Specification.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }

            var search2 = from a in search
                          join b in _producttypeRepository.AsQueryable() on a.TypeId equals b.Id
                          orderby a.ExpirationDate
                          select DtoHelper.TransProductDto(a, b);
            productExportData = search2.Select(s => new ProductExport
            {
                Name = s.Name,
                Specification = s.Specification,
                ExpirationDate = s.ExpirationDate,
                Num = s.Num,
                Formulations = s.Formulations,
                Manufacturer = s.Manufacturer,
                Price = s.Price,
                Unit = s.Unit
            }).ToList();
            return productExportData;
        }

        /// <summary>
        /// 导出出入库记录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> ExportStatisticsProduct([FromQuery] BaseInput input)
        {
            List<ProductStatisticsDto> data = this.GetStatisticsProduct(input);
            var fileStream = ExcelHelper.ParseListToExcel(data);
            var fileName = HttpUtility.UrlEncode("药品出入库统计" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx", Encoding.GetEncoding("UTF-8"));
            return await Task.FromResult(
             new FileContentResult(fileStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
             {
                 FileDownloadName = fileName
             });
        }
        /// <summary>
        /// 查询出入库统计-导出
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private List<ProductStatisticsDto> GetStatisticsProduct(BaseInput input)
        {
            List<ProductStatisticsDto> ProductStatistics = new List<ProductStatisticsDto>();
            var search = _productRepository.Include(s => s.PurchaseOrderDetails)
                .Include(s => s.SaleOrderDetails)
                .AsQueryable();
            search = search.Where(x => x.ExpirationDate >= DateTime.Now);
            if (!string.IsNullOrEmpty(input.keyword))
            {
                search = search.Where(m => m.Name.Contains(input.keyword)
                || m.Code.Contains(input.keyword));
            }
            if (input.typeid > 0)
            {
                var typeIds = _producttypeRepository.Where(m => m.ParentIds.Contains(input.typeid.ToString())).Select(m => m.Id).ToList();
                typeIds.Add(input.typeid);
                search = search.Where(m => typeIds.Contains(m.TypeId));
            }
            if (input.sdate != null)
            {
                search = search.Where(m => m.CreatedTime >= input.sdate);
            }
            if (input.edate != null)
            {
                var edate = input.edate?.AddDays(1).Date;
                search = search.Where(m => m.CreatedTime < edate);
            }
            search.OrderByDescending(x => x.CreatedTime);
            foreach (cts_Product s in search)
            {
                ProductStatisticsDto productStatisticsDto = new ProductStatisticsDto();
                productStatisticsDto.Id = s.Id;
                productStatisticsDto.Name = s.Name;
                productStatisticsDto.Num = s.Num;
                productStatisticsDto.Specification = s.Specification;
                productStatisticsDto.Formulations = s.Formulations;
                productStatisticsDto.Description = s.Description;
                productStatisticsDto.ExpirationDate = s.ExpirationDate;
                productStatisticsDto.PurchaseVisits = s.PurchaseOrderDetails.GroupBy(s => s.PurchaseOrderId).Count();
                productStatisticsDto.PurchaseNum = s.PurchaseOrderDetails.Sum(m => m.Num);
                productStatisticsDto.PurchaseAmount = s.PurchaseOrderDetails.Sum(m => m.Price * m.Num);
                productStatisticsDto.SaleVisits = s.SaleOrderDetails.GroupBy(s => s.SaleOrderId).Count();
                productStatisticsDto.SaleNum = s.SaleOrderDetails.Sum(m => m.Num);
                productStatisticsDto.SaleAmount = s.SaleOrderDetails.Sum(m => m.Price * m.Num);
                ProductStatistics.Add(productStatisticsDto);
            }
            return ProductStatistics;
        }
        #endregion


    }
}
